import React, { useCallback, useEffect, useMemo, useState } from 'react';

import { ShareDocEditorData } from '../types';

let dataState: ShareDocEditorData = {};

const dispatchMap = new Map<
  React.Dispatch<React.SetStateAction<ShareDocEditorData>>,
  Array<keyof ShareDocEditorData>
>();

const defaultProps: Array<keyof Omit<ShareDocEditorData, keyof ShareDocEditorData>> = [];

export const useShareDocEditorData = <T extends keyof ShareDocEditorData>(
  props: Array<keyof Pick<ShareDocEditorData, T>> = defaultProps,
) => {
  const [state, setState] = useState(dataState);
  const propsString = props.join('&');
  const data = useMemo(() => {
    if (!props.length) return { ...state };
    return props.reduce(
      (result, prop) => {
        result[prop] = state[prop];
        return result;
      },
      {} as Pick<ShareDocEditorData, T>,
    );
  }, [propsString, state]);
  const setData = useCallback(
    (data: Partial<Pick<ShareDocEditorData, T>>) => {
      const isFull = !props.length;
      const dataStatePatch: Partial<ShareDocEditorData> = {};
      !isFull &&
        Reflect.ownKeys(data).forEach((key) => {
          const k = key as T;
          if (props.includes(k)) dataStatePatch[k] = data[k];
        });
      dataState = { ...dataState, ...(isFull ? data : dataStatePatch) };
      const keys = Object.keys(data || {}) as Array<keyof ShareDocEditorData>;
      //@ts-ignore
      for (const [dispatch, items] of dispatchMap.entries())
        (keys.some((key) => items.includes(key)) || !items.length) && dispatch(dataState);
    },
    [propsString],
  );
  useEffect(() => {
    dispatchMap.set(setState, props);
    return () => {
      dispatchMap.delete(setState);
    };
  }, [propsString]);

  return [data, setData] as const;
};
